﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;

namespace CommonlyUtils
{
    /// <summary>
    /// 日志操作类
    /// 文件功能描述：公共类，系统日志，通过本类可以快速地将系统日志写入磁盘文件
    /// 依赖说明：通过Config读取输出日志等级 
    /// 异常处理：捕获异常，当写日志异常时，忽略错误
    /// </summary>
    public class LogHelper
    {
        /// <summary>
        /// The obj. 
        /// </summary>
        private static readonly object obj = new object();

        /// <summary>
        /// 日志文件路径，默认为当前应用程序下log子目录,包含路径结束符号
        /// </summary>
        private static string LogFilePath
        {
            get
            {
                return AppDomain.CurrentDomain.BaseDirectory + "Logs" + Path.DirectorySeparatorChar;
            }
        }

        /// <summary>
        /// 写日志等级设置，默认只写“警告”以上等级的日志
        /// </summary>
        private static LogLevel LogWriteLevel
        {
            get
            {
                return LogLevel.H调试信息;
            }
        }

        /// <summary>
        /// 写日志文件
        /// </summary>
        /// <param name="logLevel">日志等级</param>
        /// <param name="content">日志内容</param>
        public static void WriteLog(LogLevel logLevel, string content)
        {
            if (logLevel > LogWriteLevel)
            {
                return;
            }
            Task.Run(() =>
            {
                Log(content, LogFilePath);
            });
            //Action action = () => Log(content, LogFilePath);
            //action.BeginInvoke(null, null);
        }


        /// <summary>
        /// Writes the log.
        /// </summary>
        /// <param name="content">The content.</param>
        /// <param name="logLevel">The log level.</param>
        public static void WriteLog(string content, LogLevel logLevel = LogLevel.H调试信息)
        {
            Task.Run(() =>
            {
                Log(content, LogFilePath, logLevel);
            });
            //Action action = () => Log(content, LogFilePath, logLevel);
            //action.Invoke();
        }

        /// <summary>
        /// Writes the log.
        /// </summary>
        /// <param name="ex">The ex.</param>
        /// <param name="remark">The remark.</param>
        /// <param name="logLevel">The log level.</param>
        public static void WriteLog(Exception ex, string remark, LogLevel logLevel = LogLevel.D错误事件)
        {
            if (logLevel > LogWriteLevel)
            {
                return;
            }
            else
            {
                WriteLog(ex, null, remark, logLevel);
            }
        }


        /// <summary>
        /// 记录异常文本日志
        /// </summary>
        /// <param name="ex">异常</param>
        /// <param name="path">日志路径</param>
        /// <param name="remark">备注</param>
        /// <param name="logLevel">The log level.</param>
        public static void WriteLog(Exception ex, string path, string remark, LogLevel logLevel = LogLevel.H调试信息)
        {
            var errormessage = CreateErrorMessage(ex, remark);
            WriteLog(errormessage.ToString(), path ?? Path.Combine(LogFilePath, "ExceptionLog"), logLevel);
        }

        /// <summary>
        /// 自定义文件夹目录写日志
        /// </summary>
        /// <param name="content">The content.</param>
        /// <param name="DirNames">The dir names.</param>
        /// <param name="logLevel">The log level.</param>
        public static void CallLog(string content, string DirNames, LogLevel logLevel = LogLevel.H调试信息)
        {
            if (logLevel > LogWriteLevel)
            {
                return;
            }
            Task.Run(() => 
            {
                Log(content, Path.Combine(LogFilePath, DirNames), logLevel);
            });

            //Action action = () =>
            //action.BeginInvoke(null, null);
        }

        /// <summary>
        /// 记录文本日志
        /// </summary>
        /// <param name="content">日志内容</param>
        /// <param name="path">日志路径</param>
        /// <param name="logLevel">The log level.</param>
        public static void WriteLog(string content, string path, LogLevel logLevel = LogLevel.H调试信息)
        {
            Task.Run(() =>
            {
                Log(content, path, logLevel);
            });
        }


        /// <summary>
        /// Logs the specified content.
        /// </summary>
        /// <param name="content">The content.</param>
        /// <param name="path">The path.</param>
        /// <param name="logLevel">The log level.</param>
        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
        internal static bool Log(string content, string path, LogLevel logLevel = LogLevel.H调试信息)
        {
            lock (obj)
            {
                try
                {
                    TextWriter textWriter = new TextWriter(path);
                    return
                        textWriter.WriteLog("==========================================" + Environment.NewLine +
                            DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffff") + "  " + logLevel.ToString() + Environment.NewLine + content
                            + Environment.NewLine);
                }
                catch (Exception)
                {
                    return false;
                }
            }
        }

        /// <summary>
        /// 创建异常消息
        /// </summary>
        /// <param name="ex">异常信息</param>
        /// <param name="remark">备注</param>
        /// <returns>结果</returns>
        private static StringBuilder CreateErrorMessage(Exception ex, string remark)
        {
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append("************************Exception Start************************");
            string newLine = Environment.NewLine;
            stringBuilder.Append(newLine);
            stringBuilder.AppendLine("Exception Remark：" + remark);
            Exception innerException = ex.InnerException;
            stringBuilder.AppendFormat("Exception Date:{0}{1}", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fffff"), Environment.NewLine);
            if (innerException != null)
            {
                stringBuilder.AppendFormat("Inner Exception Type:{0}{1}", innerException.GetType(), newLine);
                stringBuilder.AppendFormat("Inner Exception Message:{0}{1}", innerException.Message, newLine);
                stringBuilder.AppendFormat("Inner Exception Source:{0}{1}", innerException.Source, newLine);
                stringBuilder.AppendFormat("Inner Exception StackTrace:{0}{1}", innerException.StackTrace, newLine);
            }
            stringBuilder.AppendFormat("Exception Type:{0}{1}", ex.GetType(), newLine);
            stringBuilder.AppendFormat("Exception Message:{0}{1}", ex.Message, newLine);
            stringBuilder.AppendFormat("Exception Source:{0}{1}", ex.Source, newLine);
            stringBuilder.AppendFormat("Exception StackTrace:{0}{1}", ex.StackTrace, newLine);
            stringBuilder.Append("************************Exception End**************************");
            stringBuilder.Append(newLine);
            return stringBuilder;
        }
    }
    /// <summary>
    /// 系统日志等级，按照Syslog的标准，将日志等级分为八个等级
    /// </summary>
    public enum LogLevel
    {
        /// <summary>
        /// Emergency
        /// </summary>
        A系统不可用 = 0,
        /// <summary>
        /// Alert
        /// </summary>
        B紧急事件 = 1,
        /// <summary>
        /// Critical
        /// </summary>
        C关键事件 = 2,
        /// <summary>
        /// Error
        /// </summary>
        D错误事件 = 3,
        /// <summary>
        /// Warning
        /// </summary>
        E警告事件 = 4,
        /// <summary>
        /// Notice
        /// </summary>
        F重要事件 = 5,
        /// <summary>
        /// Information
        /// </summary>
        G有用信息 = 6,
        /// <summary>
        /// Debug
        /// </summary>
        H调试信息 = 7
    }

    /// <summary>
    /// 写入文本
    /// </summary>
    internal class TextWriter
    {
        /// <summary>
        /// 写入文件路径
        /// </summary>
        private readonly string fileName;

        /// <summary>
        /// Initializes a new instance of the <see cref="TextWriter"/> class.
        /// </summary>
        public TextWriter()
            : this(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Logs"))
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="TextWriter"/> class.
        /// </summary>
        /// <param name="fileName">
        /// The file name.
        /// </param>
        public TextWriter(string fileName)
        {
            this.fileName = fileName;
        }

        /// <summary>
        /// 写入日志
        /// </summary>
        /// <param name="logInfo">内容 </param>
        /// <returns>结果</returns>
        internal bool WriteLog(string logInfo)
        {
            if (string.IsNullOrWhiteSpace(logInfo))
            {
                return false;
            }

            DateTime timeStamp = DateTime.Now;
            string path = this.GetFileMainPath(timeStamp);
            FileInfo lastFile = GetLastAccessFile(path, timeStamp);

            using (FileStream fileStream = GetFileStream(lastFile, path, timeStamp))
            {
                using (StreamWriter sw = new StreamWriter(fileStream))
                {
                    sw.BaseStream.Seek(0, SeekOrigin.End);
                    sw.Write(logInfo);
                    sw.Flush();
                    return true;
                }
            }
        }

        /// <summary>
        /// 获取文件路径
        /// </summary>
        /// <param name="timeStamp">timeStamp</param>
        /// <returns>path</returns>
        private string GetFileMainPath(DateTime timeStamp)
        {
            return Path.Combine(this.fileName, timeStamp.ToString("yyyyMMdd"));
        }

        /// <summary>
        /// 获取最后写入日志的文件
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="timeStamp">timeStamp</param>
        /// <returns>FileInfo</returns>
        private static FileInfo GetLastAccessFile(string path, DateTime timeStamp)
        {
            FileInfo result = null;
            DirectoryInfo directoryInfo = new DirectoryInfo(path);
            if (directoryInfo.Exists)
            {
                FileInfo[] fileInfos = directoryInfo.GetFiles();
                foreach (FileInfo fileInfo in fileInfos)
                {
                    if (timeStamp.Hour == fileInfo.CreationTime.Hour)
                    {
                        result = fileInfo;
                        break;
                    }
                }
            }
            else
            {
                directoryInfo.Create();
            }

            return result;
        }

        /// <summary>
        /// 获取文件流
        /// </summary>
        /// <param name="fileInfo">lastFile</param>
        /// <param name="path">path</param>
        /// <param name="timeStamp">timeStamp</param>
        /// <returns>FileStream</returns>
        private static FileStream GetFileStream(FileInfo fileInfo, string path, DateTime timeStamp)
        {
            FileStream result;
            if (fileInfo == null)
            {
                try
                {
                    result = CreateFile(path, GetFileMainName(timeStamp));
                }
                catch (Exception)
                {
                    return null;
                }
            }
            else if (IsOutOfTimeMaxLength(fileInfo.CreationTime, timeStamp))
            {
                result = CreateFile(path, GetFileMainName(timeStamp));
            }
            else
            {
                try
                {
                    result = fileInfo.OpenWrite();
                }
                catch
                {
                    result = CreateFile(path, GetFileMainName(timeStamp));
                }
            }

            return result;
        }

        /// <summary>
        /// 创建文件
        /// </summary>
        /// <param name="path">路径</param>
        /// <param name="fileName1">名称</param>
        /// <returns>FileStream</returns>
        private static FileStream CreateFile(string path, string fileName1)
        {
            return File.Create($@"{path}\{fileName1}.log");
        }

        /// <summary>
        /// 获取文件名称
        /// </summary>
        /// <param name="timeStamp">timeStamp</param>
        /// <returns>HHmmssfff</returns>
        private static string GetFileMainName(DateTime timeStamp)
        {
            return timeStamp.ToString("HH");
        }

        /// <summary>
        /// IsOutOfTimeMaxLength
        /// </summary>
        /// <param name="creationTime">creationTime</param>
        /// <param name="timeStamp">timeStamp</param>
        /// <returns>bool</returns>
        private static bool IsOutOfTimeMaxLength(DateTime creationTime, DateTime timeStamp)
        {
            return Math.Abs((creationTime - timeStamp).TotalHours) >= 1;
        }
    }
}
